/**
 * 
 */
package org.statewalker.parser.xhtml.grammar;

import java.util.HashSet;
import java.util.Set;

import org.statewalker.dict.BaseDict;
import org.statewalker.tokenizer.text.TextDict;
import org.statewalker.tokenizer.xml.XMLDict;
import org.statewalker.validator.AbstractGrammar;
import org.statewalker.validator.ValidatorGrammarConfig;

/**
 * http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd
 * <p>
 * TODO: Add support of element prohibition -
 * http://www.w3.org/TR/xhtml1/#prohibitions
 * </p>
 * 
 * @author kotelnikov
 */
public class XHTMLGrammar extends AbstractGrammar {

    public final Set<String> EMPTY_TAGS = new HashSet<String>();

    public XHTMLGrammar(ValidatorGrammarConfig config) {
        this(config, BaseDict.ROOT);
    }

    private XHTMLGrammar(ValidatorGrammarConfig config, String root) {
        super(config, root);
        EMPTY_TAGS.add(XHTMLDict.AREA);
        EMPTY_TAGS.add(XHTMLDict.BASE);
        EMPTY_TAGS.add(XHTMLDict.BR);
        EMPTY_TAGS.add(XHTMLDict.COL);
        EMPTY_TAGS.add(XHTMLDict.HR);
        EMPTY_TAGS.add(XHTMLDict.IMG);
        EMPTY_TAGS.add(XHTMLDict.INPUT);
        EMPTY_TAGS.add(XHTMLDict.LINK);
        EMPTY_TAGS.add(XHTMLDict.META);
        EMPTY_TAGS.add(XHTMLDict.PARAM);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_block
    // <!ENTITY % block
    // "p | %heading; | div | %lists; | %blocktext; | fieldset | table">
    protected void addBlock(String key) {
        defineSubstates(
            key,
            XHTMLDict.P,
            XHTMLDict.DIV,
            XHTMLDict.FIELDSET,
            XHTMLDict.TABLE);
        addHeading(key);
        addLists(key);
        addBlocktext(key);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_Block
    // <!ENTITY % Block "(%block; | form | %misc;)*">
    protected void addBlockExtended(String key) {
        addBlock(key);
        defineSubstates(key, XHTMLDict.FORM);
        addMisc(key);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_blocktext
    // <!ENTITY % blocktext "pre | hr | blockquote | address">
    protected void addBlocktext(String key) {
        defineSubstates(
            key,
            XHTMLDict.PRE,
            XHTMLDict.HR,
            XHTMLDict.BLOCKQUOTE,
            XHTMLDict.ADDRESS);
    }

    // 
    protected void addCDATA(String key) {
        defineSubstates(key, XMLDict.CDATA);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_Flow
    // <!ENTITY % Flow "(#PCDATA | %block; | form | %inline; | %misc;)*">
    protected void addFlow(String key) {
        defineSubstates(key, XHTMLDict.FORM);
        addPCDATA(key);
        addBlock(key);
        addInline(key);
        addMisc(key);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_fontstyle
    // <!ENTITY % fontstyle "tt | i | b | big | small ">
    protected void addFontstyle(String key) {
        defineSubstates(
            key,
            XHTMLDict.TT,
            XHTMLDict.I,
            XHTMLDict.B,
            XHTMLDict.BIG,
            XHTMLDict.SMALL);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_heading
    // <!ENTITY % heading "h1|h2|h3|h4|h5|h6">
    protected void addHeading(String key) {
        defineSubstates(
            key,
            XHTMLDict.H1,
            XHTMLDict.H2,
            XHTMLDict.H3,
            XHTMLDict.H4,
            XHTMLDict.H5,
            XHTMLDict.H6);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_inline
    // <!ENTITY % inline
    // "a | %special; | %fontstyle; | %phrase; | %inline.forms;">
    protected void addInline(String key) {
        defineSubstates(key, XHTMLDict.A);
        addSpecial(key);
        addFontstyle(key);
        addPhrase(key);
        addInlineForms(key);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_Inline
    // <!ENTITY % Inline "(#PCDATA | %inline; | %misc.inline;)*">
    protected void addInlineExtended(String key) {
        addPCDATA(key);
        addInline(key);
        addMiscInline(key);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_inline.forms
    // <!ENTITY % inline.forms "input | select | textarea | label | button">
    protected void addInlineForms(String key) {
        defineSubstates(
            key,
            XHTMLDict.INPUT,
            XHTMLDict.SELECT,
            XHTMLDict.TEXTAREA,
            XHTMLDict.LABEL,
            XHTMLDict.BUTTON);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_lists
    // <!ENTITY % lists "ul | ol | dl">
    protected void addLists(String key) {
        defineSubstates(key, XHTMLDict.UL, XHTMLDict.OL, XHTMLDict.DL);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_misc
    // <!ENTITY % misc "noscript | %misc.inline;">
    protected void addMisc(String key) {
        defineSubstates(key, XHTMLDict.NOSCRIPT);
        addMiscInline(key);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_misc.inline
    // <!ENTITY % misc.inline "ins | del | script">
    protected void addMiscInline(String key) {
        defineSubstates(key, XHTMLDict.INS, XHTMLDict.DEL, XHTMLDict.SCRIPT);
        if (useExtendedRules()) {
            defineSubstates(key, XHTMLDict.STYLE);
        }
    }

    protected void addOptions(String key) {
        String option = XHTMLDict.OPTION;
        defineTransition(key, BaseDict.INITIAL, option, option);
        defineTransition(key, option, option, option);
        // The field can not be empty.
        // It contains at least one option by default.
        defineTransition(key, BaseDict.INITIAL, BaseDict.ANY, option);
    }

    // 
    protected void addPCDATA(String key) {
        defineSubstates(
            key,
            TextDict.SPECIAL,
            TextDict.SPACES,
            TextDict.WORD,
            TextDict.EOL,
            XMLDict.ENTITY,
            XMLDict.CDATA);
        markKeyAsAcceptingSpaces(key);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_phrase
    // <!ENTITY % phrase
    // "em | strong | dfn | code | q | samp | kbd | var | cite | abbr | acronym | sub | sup ">
    protected void addPhrase(String key) {
        defineSubstates(
            key,
            XHTMLDict.EM,
            XHTMLDict.STRONG,
            XHTMLDict.DFN,
            XHTMLDict.CODE,
            XHTMLDict.Q,
            XHTMLDict.SAMP,
            XHTMLDict.KBD,
            XHTMLDict.VAR,
            XHTMLDict.CITE,
            XHTMLDict.ABBR,
            XHTMLDict.ACRONYM,
            XHTMLDict.SUB,
            XHTMLDict.SUP);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_special
    // <!ENTITY % special "%special.pre; | object | img ">
    protected void addSpecial(String key) {
        addSpecialPre(key);
        defineSubstates(key, XHTMLDict.OBJECT, XHTMLDict.IMG);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_special.pre
    // <!ENTITY % special.pre "br | span | bdo | map">
    protected void addSpecialPre(String key) {
        defineSubstates(
            key,
            XHTMLDict.BR,
            XHTMLDict.SPAN,
            XHTMLDict.BDO,
            XHTMLDict.MAP);
    }

    protected String getBlockContainer() {
        return XHTMLDict.BODY;
    }

    private String getDefaultFormElementContainer() {
        return XHTMLDict.P;
    }

    @Override
    public void init() {
        initRoot();

        initA();
        initABBR();
        initACRONYM();
        initADDRESS();
        initAREA();
        initB();
        initBASE();
        initBDO();
        initBIG();
        initBLOCKQUOTE();
        initBODY();
        initBR();
        initBUTTON();
        initCAPTION();
        initCDATA();
        initCITE();
        initCODE();
        initCOL();
        initCOLGROUP();
        initCOMMENT();
        initDD();
        initDEL();
        initDFN();
        initDIV();
        initDL();
        initDT();
        initEM();
        initFIELDSET();
        initFORM();
        initH1();
        initH2();
        initH3();
        initH4();
        initH5();
        initH6();
        initHEAD();
        initHR();
        initHTML();
        initI();
        initIMG();
        initINPUT();
        initINS();
        initKBD();
        initLABEL();
        initLEGEND();
        initLI();
        initLINK();
        initMAP();
        initMETA();
        initNOSCRIPT();
        initOBJECT();
        initOL();
        initOPTGROUP();
        initOPTION();
        initP();
        initPARAM();
        initPCDATA();
        initPRE();
        initQ();
        initSAMP();
        initSCRIPT();
        initSELECT();
        initSMALL();
        initSPAN();
        initSTRONG();
        initSTYLE();
        initSUB();
        initSUP();
        initTABLE();
        initTBODY();
        initTD();
        initTEXTAREA();
        initTFOOT();
        initTH();
        initTHEAD();
        initTITLE();
        initTR();
        initTT();
        initUL();
        initVAR();
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_a
    // <!ELEMENT a %a.content;>
    // <!ENTITY % a.content
    // "(#PCDATA | %special; | %fontstyle; | %phrase; | %inline.forms; | %misc.inline;)*">
    protected void initA() {
        initTag(XHTMLDict.A);
        defineParent(XHTMLDict.A, XHTMLDict.P);

        // Definition of the "a.content":
        addPCDATA(XHTMLDict.A);
        addSpecial(XHTMLDict.A);
        addFontstyle(XHTMLDict.A);
        addPhrase(XHTMLDict.A);
        addInlineForms(XHTMLDict.A);
        addMiscInline(XHTMLDict.A);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_abbr
    // <!ELEMENT abbr %Inline;> <!-- abbreviation -->
    protected void initABBR() {
        initPhraseItem(XHTMLDict.ABBR);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_acronym
    // <!ELEMENT acronym %Inline;> <!-- acronym -->
    protected void initACRONYM() {
        initPhraseItem(XHTMLDict.ACRONYM);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_address
    // <!ELEMENT address %Inline;>
    protected void initADDRESS() {
        initTag(XHTMLDict.ADDRESS);
        defineParent(XHTMLDict.ADDRESS, getBlockContainer());
        addInlineExtended(XHTMLDict.ADDRESS);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_area
    // <!ELEMENT area EMPTY>
    protected void initAREA() {
        init(XHTMLDict.AREA);
        defineParent(XHTMLDict.AREA, XHTMLDict.MAP);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_b
    // <!ELEMENT b %Inline;> <!-- bold font -->
    protected void initB() {
        initPhraseItem(XHTMLDict.B);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_base
    // <!ELEMENT base EMPTY>
    protected void initBASE() {
        init(XHTMLDict.BASE);
        defineParent(XHTMLDict.BASE, XHTMLDict.HEAD);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_bdo
    // <!ELEMENT bdo %Inline;> <!-- I18N BiDi over-ride -->
    protected void initBDO() {
        initTag(XHTMLDict.BDO);
        initPhraseItem(XHTMLDict.BDO);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_big
    // <!ELEMENT big %Inline;> <!-- bigger font -->
    protected void initBIG() {
        initPhraseItem(XHTMLDict.BIG);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_blockquote
    // <!ELEMENT blockquote %Block;>
    protected void initBLOCKQUOTE() {
        initTag(XHTMLDict.BLOCKQUOTE);
        defineParent(XHTMLDict.BLOCKQUOTE, getBlockContainer());
        addBlockExtended(XHTMLDict.BLOCKQUOTE);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_body
    // <!ELEMENT body %Block;>
    protected void initBODY() {
        initTag(XHTMLDict.BODY);
        defineParent(XHTMLDict.BODY, XHTMLDict.HTML);
        defineTransition(XHTMLDict.BODY, BaseDict.ANY, BaseDict.ERROR);
        defineTransition(
            XHTMLDict.BODY,
            BaseDict.ERROR,
            BaseDict.ANY,
            BaseDict.INITIAL);
        addBlockExtended(XHTMLDict.BODY);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_br
    // <!ELEMENT br EMPTY> <!-- forced line break -->
    protected void initBR() {
        init(XHTMLDict.BR);
        defineParent(XHTMLDict.BR, XHTMLDict.P);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_button
    // <!ELEMENT button %button.content;> <!-- push button -->
    //
    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_button.content:
    // <!ENTITY % button.content
    // "(#PCDATA | p | %heading; | div | %lists; | %blocktext; |    table | %special; | %fontstyle; | %phrase; | %misc;)*">
    protected void initBUTTON() {
        initTag(XHTMLDict.BUTTON);
        String parent = getDefaultFormElementContainer();
        defineParent(XHTMLDict.BUTTON, parent);

        // Definition of the "button.content":
        defineSubstates(
            XHTMLDict.BUTTON,
            XHTMLDict.P,
            XHTMLDict.DIV,
            XHTMLDict.TABLE);
        addPCDATA(XHTMLDict.BUTTON);
        addHeading(XHTMLDict.BUTTON);
        addLists(XHTMLDict.BUTTON);
        addBlocktext(XHTMLDict.BUTTON);
        addSpecial(XHTMLDict.BUTTON);
        addFontstyle(XHTMLDict.BUTTON);
        addPhrase(XHTMLDict.BUTTON);
        addMisc(XHTMLDict.BUTTON);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_caption
    // <!ELEMENT caption %Inline;>
    protected void initCAPTION() {
        initTag(XHTMLDict.CAPTION);
        defineParent(XHTMLDict.CAPTION, XHTMLDict.TABLE);
        addInlineExtended(XHTMLDict.CAPTION);
    }

    protected void initCDATA() {
        init(XMLDict.CDATA);
        defineParent(XMLDict.CDATA, XHTMLDict.P);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_cite
    // <!ELEMENT cite %Inline;> <!-- citation -->
    protected void initCITE() {
        initPhraseItem(XHTMLDict.CITE);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_code
    // <!ELEMENT code %Inline;> <!-- program code -->
    protected void initCODE() {
        initPhraseItem(XHTMLDict.CODE);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_col
    // <!ELEMENT col EMPTY>
    protected void initCOL() {
        init(XHTMLDict.COL);
        defineParent(XHTMLDict.COL, XHTMLDict.TABLE);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_colgroup
    // <!ELEMENT colgroup (col)*>
    protected void initCOLGROUP() {
        initTag(XHTMLDict.COLGROUP);
        defineParent(XHTMLDict.COLGROUP, XHTMLDict.TABLE);
        defineSubstates(XHTMLDict.COLGROUP, XHTMLDict.COL);
    }

    protected void initCOMMENT() {
        init(XMLDict.COMMENT);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_dd
    // <!ELEMENT dd %Flow;>
    protected void initDD() {
        initTag(XHTMLDict.DD);
        defineParent(XHTMLDict.DD, XHTMLDict.DL);
        addFlow(XHTMLDict.DD);
        if (useExtendedRules()) {
            defineTransition(XHTMLDict.DD, XHTMLDict.DD, BaseDict.FINAL);
            defineTransition(XHTMLDict.DD, XHTMLDict.DT, BaseDict.FINAL);
        }
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_del
    // <!ELEMENT del %Flow;>
    protected void initDEL() {
        initTag(XHTMLDict.DEL);
        defineParent(XHTMLDict.DEL, getBlockContainer());
        addFlow(XHTMLDict.DEL);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_dfn
    // <!ELEMENT dfn %Inline;> <!-- definitional -->
    protected void initDFN() {
        initPhraseItem(XHTMLDict.DFN);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_div
    // <!ELEMENT div %Flow;> <!-- generic language/style container -->
    protected void initDIV() {
        initTag(XHTMLDict.DIV);
        defineParent(XHTMLDict.DIV, getBlockContainer());
        addFlow(XHTMLDict.DIV);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_dl
    // <!ELEMENT dl (dt|dd)+>
    protected void initDL() {
        initTag(XHTMLDict.DL);
        defineParent(XHTMLDict.DL, getBlockContainer());
        defineSubstates(XHTMLDict.DL, XHTMLDict.DD, XHTMLDict.DT);
        // By default (if it is not a "dd" or "dt" tag) the list should
        // create a "dd" element.
        defineMandatoryTransition(XHTMLDict.DL, BaseDict.INITIAL, XHTMLDict.DD);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_dt
    // <!ELEMENT dt %Inline;>
    protected void initDT() {
        initTag(XHTMLDict.DT);
        defineParent(XHTMLDict.DT, XHTMLDict.DL);
        addInlineExtended(XHTMLDict.DT);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_em
    // <!ELEMENT em %Inline;> <!-- emphasis -->
    protected void initEM() {
        initPhraseItem(XHTMLDict.EM);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_fieldset
    // <!ELEMENT fieldset (#PCDATA | legend | %block; | form | %inline; |
    // %misc;)*>
    protected void initFIELDSET() {
        initTag(XHTMLDict.FIELDSET);
        defineParent(XHTMLDict.FIELDSET, getBlockContainer());
        defineSubstates(XHTMLDict.FIELDSET, XHTMLDict.LEGEND, XHTMLDict.FORM);
        addPCDATA(XHTMLDict.FIELDSET);
        addBlock(XHTMLDict.FIELDSET);
        addInline(XHTMLDict.FIELDSET);
        addMisc(XHTMLDict.FIELDSET);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_form
    // <!ELEMENT form %form.content;> <!-- forms shouldn't be nested -->
    // <!ENTITY % form.content "(%block; | %misc;)*">
    protected void initFORM() {
        initTag(XHTMLDict.FORM);
        defineParent(XHTMLDict.FORM, getBlockContainer());
        // Definition of the "form.content":
        addBlock(XHTMLDict.FORM);
        addMisc(XHTMLDict.FORM);

        // if (useExtendedRules()) {
        // defineSubstates(
        // XHTMLDict.FORM,
        // XHTMLDict.BUTTON,
        // XHTMLDict.INPUT,
        // XHTMLDict.LABEL,
        // XHTMLDict.SELECT,
        // XHTMLDict.TEXTAREA);
        // }
    }

    protected void initH1() {
        initHN(XHTMLDict.H1);
    }

    protected void initH2() {
        initHN(XHTMLDict.H2);
    }

    protected void initH3() {
        initHN(XHTMLDict.H3);
    }

    protected void initH4() {
        initHN(XHTMLDict.H4);
    }

    protected void initH5() {
        initHN(XHTMLDict.H5);
    }

    protected void initH6() {
        initHN(XHTMLDict.H6);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_head
    // <!ELEMENT head (%head.misc;, ((title, %head.misc;, (base, %head.misc;)?)
    // | (base, %head.misc;, (title, %head.misc;))))>
    // 
    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_head.misc
    // <!ENTITY % head.misc "(script|style|meta|link|object)*">
    protected void initHEAD() {
        initTag(XHTMLDict.HEAD);
        defineParent(XHTMLDict.HEAD, XHTMLDict.HTML);
        defineSubstates(XHTMLDict.HEAD, XHTMLDict.TITLE, XHTMLDict.BASE);

        // Defintion of the "head.misc":
        defineSubstates(
            XHTMLDict.HEAD,
            XHTMLDict.SCRIPT,
            XHTMLDict.STYLE,
            XHTMLDict.META,
            XHTMLDict.LINK,
            XHTMLDict.OBJECT);

        if (useExtendedRules()) {
            // The second "head" declaration
            defineTransition(XHTMLDict.HEAD, XHTMLDict.HEAD, BaseDict.ERROR);
            defineTransition(
                XHTMLDict.HEAD,
                BaseDict.ERROR,
                BaseDict.ANY,
                BaseDict.INITIAL);
        }
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_h1
    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_h2
    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_h3
    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_h4
    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_h5
    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_h6
    // <!ELEMENT h1 %Inline;>
    protected void initHN(String key) {
        initTag(key);
        defineParent(key, getBlockContainer());
        addInlineExtended(key);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_hr
    // <!ELEMENT hr EMPTY>
    protected void initHR() {
        init(XHTMLDict.HR);
        defineParent(XHTMLDict.HR, getBlockContainer());
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_html
    // <!ELEMENT html (head, body)>
    protected void initHTML() {
        initTag(XHTMLDict.HTML);
        defineParent(XHTMLDict.HTML, getROOT());
        defineMandatoryTransition(
            XHTMLDict.HTML,
            BaseDict.INITIAL,
            XHTMLDict.HEAD);
        defineMandatoryTransition(
            XHTMLDict.HTML,
            XHTMLDict.HEAD,
            XHTMLDict.BODY);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_i
    // <!ELEMENT i %Inline;> <!-- italic font -->
    protected void initI() {
        initPhraseItem(XHTMLDict.I);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_img
    // <!ELEMENT img EMPTY>
    protected void initIMG() {
        init(XHTMLDict.IMG);
        defineParent(XHTMLDict.IMG, XHTMLDict.P);
        markKeyAsAcceptingSpaces(XHTMLDict.IMG);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_input
    // <!ELEMENT input EMPTY> <!-- form control -->
    protected void initINPUT() {
        init(XHTMLDict.INPUT);
        String parent = getDefaultFormElementContainer();
        defineParent(XHTMLDict.INPUT, parent);
        addInlineExtended(XHTMLDict.INPUT);
        // Don't allow to put one input into another...
        defineTransition(XHTMLDict.INPUT, XHTMLDict.INPUT, BaseDict.FINAL);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_ins
    // <!ELEMENT ins %Flow;>
    protected void initINS() {
        initTag(XHTMLDict.INS);
        defineParent(XHTMLDict.INS, getBlockContainer());
        addFlow(XHTMLDict.INS);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_kbd
    // <!ELEMENT kbd %Inline;> <!-- something user would type -->
    protected void initKBD() {
        initPhraseItem(XHTMLDict.KBD);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_label
    // <!ELEMENT label %Inline;>
    protected void initLABEL() {
        initTag(XHTMLDict.LABEL);
        String parent = getDefaultFormElementContainer();
        defineParent(XHTMLDict.LABEL, parent);
        addInlineExtended(XHTMLDict.LABEL);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_legend
    // <!ELEMENT legend %Inline;> <!-- fieldset label -->
    protected void initLEGEND() {
        initTag(XHTMLDict.LEGEND);
        defineParent(XHTMLDict.LEGEND, XHTMLDict.FIELDSET);
        addInlineExtended(XHTMLDict.LEGEND);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_li
    // <!ELEMENT li %Flow;>
    protected void initLI() {
        initTag(XHTMLDict.LI);
        defineParent(XHTMLDict.LI, XHTMLDict.UL);
        addFlow(XHTMLDict.LI);
        if (useExtendedRules()) {
            defineTransition(XHTMLDict.LI, XHTMLDict.LI, BaseDict.FINAL);
        }
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_link
    // <!ELEMENT link EMPTY>
    protected void initLINK() {
        init(XHTMLDict.LINK);
        defineParent(XHTMLDict.LINK, XHTMLDict.HEAD);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_ol
    // <!ELEMENT ol (li)+>
    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_ul
    // <!ELEMENT ul (li)+>
    private void initList(String key) {
        initTag(key);
        defineParent(key, getBlockContainer());
        defineSubstates(key, XHTMLDict.LI);
        defineMandatoryTransition(key, BaseDict.INITIAL, XHTMLDict.LI);
        if (useExtendedRules()) {
            // Open a "li" element if there is a new list
            defineTransition(key, XHTMLDict.UL, XHTMLDict.LI);
            defineTransition(key, XHTMLDict.OL, XHTMLDict.LI);
            defineTransition(key, XHTMLDict.DL, XHTMLDict.LI);
        }
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_map
    // <!ELEMENT map ((%block; | form | %misc;)+ | area+)>
    protected void initMAP() {
        initTag(XHTMLDict.MAP);
        defineParent(XHTMLDict.MAP, getBlockContainer());
        addBlock(XHTMLDict.MAP);
        defineSubstates(XHTMLDict.MAP, XHTMLDict.FORM, XHTMLDict.AREA);
        addMisc(XHTMLDict.MAP);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_meta
    // <!ELEMENT meta EMPTY>
    protected void initMETA() {
        init(XHTMLDict.META);
        defineParent(XHTMLDict.META, XHTMLDict.HEAD);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_noscript
    // <!ELEMENT noscript %Block;>
    protected void initNOSCRIPT() {
        initTag(XHTMLDict.NOSCRIPT);
        defineParent(XHTMLDict.NOSCRIPT, getBlockContainer());
        addBlockExtended(XHTMLDict.NOSCRIPT);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_object
    // <!ELEMENT object (#PCDATA | param | %block; | form | %inline; | %misc;)*>
    protected void initOBJECT() {
        initTag(XHTMLDict.OBJECT);
        defineParent(XHTMLDict.OBJECT, getBlockContainer());
        defineSubstates(XHTMLDict.OBJECT, XHTMLDict.PARAM, XHTMLDict.FORM);
        addPCDATA(XHTMLDict.OBJECT);
        addBlock(XHTMLDict.OBJECT);
        addInline(XHTMLDict.OBJECT);
        addMisc(XHTMLDict.OBJECT);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_ol
    // <!ELEMENT ul (li)+>
    protected void initOL() {
        initList(XHTMLDict.OL);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_optgroup
    // <!ELEMENT optgroup (option)+> <!-- option group -->
    protected void initOPTGROUP() {
        initTag(XHTMLDict.OPTGROUP);
        defineParent(XHTMLDict.OPTGROUP, XHTMLDict.SELECT);
        addOptions(XHTMLDict.OPTGROUP);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_option
    // <!ELEMENT option (#PCDATA)> <!-- selectable choice -->
    protected void initOPTION() {
        initTag(XHTMLDict.OPTION);
        defineParent(XHTMLDict.OPTION, XHTMLDict.SELECT);
        addPCDATA(XHTMLDict.OPTION);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_p
    // <!ELEMENT p %Inline;>
    protected void initP() {
        initTag(XHTMLDict.P);
        defineParent(XHTMLDict.P, getBlockContainer());
        addInlineExtended(XHTMLDict.P);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_param
    // <!ELEMENT param EMPTY>
    protected void initPARAM() {
        init(XHTMLDict.PARAM);
        defineParent(XHTMLDict.PARAM, XHTMLDict.OBJECT);
    }

    protected void initPCDATA() {
        for (String k : TextDict._ALL) {
            init(k);
            defineParent(k, XHTMLDict.P);
            markKeyAsAcceptingSpaces(k);
            markAsLeaf(k);
        }
        defineParent(XMLDict.ENTITY, XHTMLDict.P);
        markKeyAsAcceptingSpaces(XMLDict.ENTITY);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_phrase
    // <!ENTITY % phrase
    // "em | strong | dfn | code | q | samp | kbd | var | cite | abbr | acronym | sub | sup ">
    //
    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_abbr
    // <!ELEMENT abbr %Inline;> <!-- abbreviation -->
    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_acronym
    // <!ELEMENT acronym %Inline;> <!-- acronym -->
    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_b
    // <!ELEMENT b %Inline;> <!-- bold font -->
    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_bdo
    // <!ELEMENT bdo %Inline;> <!-- I18N BiDi over-ride -->
    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_big
    // <!ELEMENT big %Inline;> <!-- bigger font -->
    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_cite
    // <!ELEMENT cite %Inline;> <!-- citation -->
    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_code
    // <!ELEMENT code %Inline;> <!-- program code -->
    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_dfn
    // <!ELEMENT dfn %Inline;> <!-- definitional -->
    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_em
    // <!ELEMENT em %Inline;> <!-- emphasis -->
    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_i
    // <!ELEMENT i %Inline;> <!-- italic font -->
    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_kbd
    // <!ELEMENT kbd %Inline;> <!-- something user would type -->
    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_q
    // <!ELEMENT q %Inline;> <!-- inlined quote -->
    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_samp
    // <!ELEMENT samp %Inline;> <!-- sample -->
    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_small
    // <!ELEMENT small %Inline;> <!-- smaller font -->
    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_strong
    // <!ELEMENT strong %Inline;> <!-- strong emphasis -->
    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_sub
    // <!ELEMENT sub %Inline;> <!-- subscript -->
    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_sup
    // <!ELEMENT sup %Inline;> <!-- superscript -->
    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_tt
    // <!ELEMENT tt %Inline;> <!-- fixed pitch font -->
    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_var
    // <!ELEMENT var %Inline;> <!-- variable -->
    private void initPhraseItem(String key) {
        initTag(key);
        defineParent(key, XHTMLDict.P);
        addInlineExtended(key);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_pre
    // <!ELEMENT pre %pre.content;>
    // <!ENTITY % pre.content
    // "(#PCDATA | a | %fontstyle; | %phrase; | %special.pre; | %misc.inline; | %inline.forms;)*">
    protected void initPRE() {
        initTag(XHTMLDict.PRE);
        defineParent(XHTMLDict.PRE, getBlockContainer());

        // The definition of the "pre.content":
        // <!ENTITY % pre.content
        // "(#PCDATA | a | %fontstyle; | %phrase; | %special.pre; | %misc.inline; | %inline.forms;)*">
        defineSubstates(XHTMLDict.PRE, XHTMLDict.A);
        addPCDATA(XHTMLDict.PRE);
        addFontstyle(XHTMLDict.PRE);
        addPhrase(XHTMLDict.PRE);
        addSpecialPre(XHTMLDict.PRE);
        addMiscInline(XHTMLDict.PRE);
        addInlineForms(XHTMLDict.PRE);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_q
    // <!ELEMENT q %Inline;> <!-- inlined quote -->
    protected void initQ() {
        initPhraseItem(XHTMLDict.Q);
    }

    protected void initRoot() {
        defineMandatoryTransition(getROOT(), BaseDict.INITIAL, XHTMLDict.HTML);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_samp
    // <!ELEMENT samp %Inline;> <!-- sample -->
    protected void initSAMP() {
        initPhraseItem(XHTMLDict.SAMP);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_script
    // <!ENTITY % Script "CDATA">
    protected void initSCRIPT() {
        initTag(XHTMLDict.SCRIPT);
        defineParent(XHTMLDict.SCRIPT, XHTMLDict.P);
        addPCDATA(XHTMLDict.SCRIPT);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_select
    // <!ELEMENT select (optgroup|option)+> <!-- option selector -->
    protected void initSELECT() {
        initTag(XHTMLDict.SELECT);

        String parent = getDefaultFormElementContainer();
        defineParent(XHTMLDict.SELECT, parent);
        defineTransition(
            XHTMLDict.SELECT,
            BaseDict.INITIAL,
            XHTMLDict.OPTGROUP,
            XHTMLDict.OPTGROUP);
        defineTransition(
            XHTMLDict.SELECT,
            XHTMLDict.OPTGROUP,
            XHTMLDict.OPTGROUP,
            XHTMLDict.OPTGROUP);
        // This method defines "option" elements.
        addOptions(XHTMLDict.SELECT);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_small
    // <!ELEMENT small %Inline;> <!-- smaller font -->
    protected void initSMALL() {
        initPhraseItem(XHTMLDict.SMALL);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_span
    // <!ELEMENT span %Inline;> <!-- generic language/style container -->
    protected void initSPAN() {
        initPhraseItem(XHTMLDict.SPAN);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_strong
    // <!ELEMENT strong %Inline;> <!-- strong emphasis -->
    protected void initSTRONG() {
        initPhraseItem(XHTMLDict.STRONG);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_style
    // <!ELEMENT title (#PCDATA)>
    protected void initSTYLE() {
        initTag(XHTMLDict.STYLE);
        defineParent(XHTMLDict.STYLE, XHTMLDict.HEAD);
        addPCDATA(XHTMLDict.STYLE);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_sub
    // <!ELEMENT sub %Inline;> <!-- subscript -->
    protected void initSUB() {
        initPhraseItem(XHTMLDict.SUB);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_sup
    // <!ELEMENT sup %Inline;> <!-- superscript -->
    protected void initSUP() {
        initPhraseItem(XHTMLDict.SUP);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_table
    // <!ELEMENT table (caption?, (col*|colgroup*), thead?, tfoot?,
    // (tbody+|tr+))>
    protected void initTABLE() {
        initTag(XHTMLDict.TABLE);
        defineParent(XHTMLDict.TABLE, getBlockContainer());

        // Transitions from the initial state
        defineTransition(
            XHTMLDict.TABLE,
            BaseDict.INITIAL,
            XHTMLDict.CAPTION,
            XHTMLDict.CAPTION);
        defineTransition(
            XHTMLDict.TABLE,
            BaseDict.INITIAL,
            XHTMLDict.COL,
            XHTMLDict.COL);
        defineTransition(
            XHTMLDict.TABLE,
            BaseDict.INITIAL,
            XHTMLDict.COLGROUP,
            XHTMLDict.COLGROUP);
        defineTransition(
            XHTMLDict.TABLE,
            BaseDict.INITIAL,
            XHTMLDict.THEAD,
            XHTMLDict.THEAD);
        defineTransition(
            XHTMLDict.TABLE,
            BaseDict.INITIAL,
            XHTMLDict.TFOOT,
            XHTMLDict.TFOOT);
        defineTransition(
            XHTMLDict.TABLE,
            BaseDict.INITIAL,
            XHTMLDict.TBODY,
            XHTMLDict.TBODY);
        defineTransition(
            XHTMLDict.TABLE,
            BaseDict.INITIAL,
            XHTMLDict.TR,
            XHTMLDict.TR);
        defineMandatoryTransition(
            XHTMLDict.TABLE,
            BaseDict.INITIAL,
            XHTMLDict.TR);

        // Transitions from the "caption" state
        defineTransition(
            XHTMLDict.TABLE,
            XHTMLDict.CAPTION,
            XHTMLDict.COL,
            XHTMLDict.COL);
        defineTransition(
            XHTMLDict.TABLE,
            XHTMLDict.CAPTION,
            XHTMLDict.COLGROUP,
            XHTMLDict.COLGROUP);
        defineTransition(
            XHTMLDict.TABLE,
            XHTMLDict.CAPTION,
            XHTMLDict.THEAD,
            XHTMLDict.THEAD);
        defineTransition(
            XHTMLDict.TABLE,
            XHTMLDict.CAPTION,
            XHTMLDict.TFOOT,
            XHTMLDict.TFOOT);
        defineTransition(
            XHTMLDict.TABLE,
            XHTMLDict.CAPTION,
            XHTMLDict.TBODY,
            XHTMLDict.TBODY);
        defineTransition(
            XHTMLDict.TABLE,
            XHTMLDict.CAPTION,
            XHTMLDict.TR,
            XHTMLDict.TR);
        defineMandatoryTransition(
            XHTMLDict.TABLE,
            XHTMLDict.CAPTION,
            XHTMLDict.TR);

        // Transitions from the "col" state
        defineTransition(
            XHTMLDict.TABLE,
            XHTMLDict.COL,
            XHTMLDict.COL,
            XHTMLDict.COL);
        defineTransition(
            XHTMLDict.TABLE,
            XHTMLDict.COL,
            XHTMLDict.THEAD,
            XHTMLDict.THEAD);
        defineTransition(
            XHTMLDict.TABLE,
            XHTMLDict.COL,
            XHTMLDict.TFOOT,
            XHTMLDict.TFOOT);
        defineTransition(
            XHTMLDict.TABLE,
            XHTMLDict.COL,
            XHTMLDict.TBODY,
            XHTMLDict.TBODY);
        defineTransition(
            XHTMLDict.TABLE,
            XHTMLDict.COL,
            XHTMLDict.TR,
            XHTMLDict.TR);
        defineMandatoryTransition(XHTMLDict.TABLE, XHTMLDict.COL, XHTMLDict.TR);

        // Transitions from the "colgroup" state
        defineTransition(
            XHTMLDict.TABLE,
            XHTMLDict.COLGROUP,
            XHTMLDict.COLGROUP,
            XHTMLDict.COLGROUP);
        defineTransition(
            XHTMLDict.TABLE,
            XHTMLDict.COLGROUP,
            XHTMLDict.THEAD,
            XHTMLDict.THEAD);
        defineTransition(
            XHTMLDict.TABLE,
            XHTMLDict.COLGROUP,
            XHTMLDict.TFOOT,
            XHTMLDict.TFOOT);
        defineTransition(
            XHTMLDict.TABLE,
            XHTMLDict.COLGROUP,
            XHTMLDict.TBODY,
            XHTMLDict.TBODY);
        defineTransition(
            XHTMLDict.TABLE,
            XHTMLDict.COLGROUP,
            XHTMLDict.TR,
            XHTMLDict.TR);
        defineMandatoryTransition(
            XHTMLDict.TABLE,
            XHTMLDict.COLGROUP,
            XHTMLDict.TR);

        // Transitions from the "thead" state
        defineTransition(
            XHTMLDict.TABLE,
            XHTMLDict.THEAD,
            XHTMLDict.TFOOT,
            XHTMLDict.TFOOT);
        defineTransition(
            XHTMLDict.TABLE,
            XHTMLDict.THEAD,
            XHTMLDict.TBODY,
            XHTMLDict.TBODY);
        defineTransition(
            XHTMLDict.TABLE,
            XHTMLDict.THEAD,
            XHTMLDict.TR,
            XHTMLDict.TR);
        defineMandatoryTransition(
            XHTMLDict.TABLE,
            XHTMLDict.THEAD,
            XHTMLDict.TR);

        // Transitions from the "tfoot" state
        defineTransition(
            XHTMLDict.TABLE,
            XHTMLDict.TFOOT,
            XHTMLDict.TBODY,
            XHTMLDict.TBODY);
        defineTransition(
            XHTMLDict.TABLE,
            XHTMLDict.TFOOT,
            XHTMLDict.TR,
            XHTMLDict.TR);
        defineMandatoryTransition(
            XHTMLDict.TABLE,
            XHTMLDict.TFOOT,
            XHTMLDict.TR);

        // Transitions from the "tbody" state
        defineTransition(
            XHTMLDict.TABLE,
            XHTMLDict.TBODY,
            XHTMLDict.TBODY,
            XHTMLDict.TBODY);

        // Transitions from the "tr" state
        defineTransition(
            XHTMLDict.TABLE,
            XHTMLDict.TR,
            XHTMLDict.TR,
            XHTMLDict.TR);
    }

    private void initTableCell(String key) {
        initTag(key);
        defineParent(key, XHTMLDict.TR);
        addFlow(key);
        if (useExtendedRules()) {
            // These additional transitions are defined to close an existing
            // table cell when an another "tr", "td" or "th" element is
            // occurred.
            defineTransition(key, XHTMLDict.TD, BaseDict.FINAL);
            defineTransition(key, XHTMLDict.TH, BaseDict.FINAL);
            defineTransition(key, XHTMLDict.TR, BaseDict.FINAL);
        }
    }

    private void initTableRowContainer(String key) {
        initTag(key);
        defineParent(key, XHTMLDict.TABLE);
        defineSubstates(key, XHTMLDict.TR);
        defineMandatoryTransition(key, BaseDict.INITIAL, XHTMLDict.TR);
    }

    private void initTag(String key) {
        init(key);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_tbody
    // <!ELEMENT tbody (tr)+>
    protected void initTBODY() {
        initTableRowContainer(XHTMLDict.TBODY);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_td
    // <!ELEMENT td %Flow;>
    protected void initTD() {
        initTableCell(XHTMLDict.TD);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_textarea
    // <!ELEMENT textarea (#PCDATA)> <!-- multi-line text field -->
    protected void initTEXTAREA() {
        initTag(XHTMLDict.TEXTAREA);
        String parent = getDefaultFormElementContainer();
        defineParent(XHTMLDict.TEXTAREA, parent);
        addPCDATA(XHTMLDict.TEXTAREA);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_tfoot
    // <!ELEMENT tfoot (tr)+>
    protected void initTFOOT() {
        initTableRowContainer(XHTMLDict.TFOOT);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_th
    // <!ELEMENT th %Flow;>
    protected void initTH() {
        initTableCell(XHTMLDict.TH);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_thead
    // <!ELEMENT thead (tr)+>
    protected void initTHEAD() {
        initTableRowContainer(XHTMLDict.THEAD);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_title
    // <!ELEMENT title (#PCDATA)>
    protected void initTITLE() {
        initTag(XHTMLDict.TITLE);
        defineParent(XHTMLDict.TITLE, XHTMLDict.HEAD);
        addPCDATA(XHTMLDict.TITLE);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_tr
    // <!ELEMENT tr (th|td)+>
    protected void initTR() {
        initTag(XHTMLDict.TR);
        defineParent(XHTMLDict.TR, XHTMLDict.TABLE);
        defineSubstates(XHTMLDict.TR, XHTMLDict.TD, XHTMLDict.TH);
        defineMandatoryTransition(XHTMLDict.TR, BaseDict.INITIAL, XHTMLDict.TD);
        if (useExtendedRules()) {
            // Exit from the state when a new row is reported.
            // (It should create a new row in the same table and not create
            // a new embedded table);
            defineTransition(XHTMLDict.TR, XHTMLDict.TR, BaseDict.FINAL);
        }
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_tt
    // <!ELEMENT tt %Inline;> <!-- fixed pitch font -->
    protected void initTT() {
        initPhraseItem(XHTMLDict.TT);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_ul
    // <!ELEMENT ul (li)+>
    protected void initUL() {
        initList(XHTMLDict.UL);
    }

    // http://www.w3.org/TR/xhtml1/dtds.html#dtdentry_xhtml1-strict.dtd_var
    // <!ELEMENT var %Inline;> <!-- variable -->
    protected void initVAR() {
        initPhraseItem(XHTMLDict.VAR);
    }

    /**
     * This method could be overloaded in subclasses to mark that states with
     * this key could accept {@link TextDict#EOL} and {@link TextDict#SPACES}
     * tokens.
     * 
     * @param key the state key to mark as accepting space tokens
     */
    protected void markKeyAsAcceptingSpaces(String key) {
    }

    protected boolean useExtendedRules() {
        return true;
    }
}